package db

import (
	"database/sql"
	"fmt"
	"gitee.com/chejiangyi/bsfgo/core/base2"
	"gitee.com/chejiangyi/bsfgo/core/utils"
	"github.com/jmoiron/sqlx"
	"strings"
	"time"
)

type DbSource struct {
	Db *sqlx.DB
}

func (c *DbSource) CreatePool() *DbSource {
	return c.CreatePool2(base2.DbConfig{
		DriverName:      base2.BsfConfigInstance.GetDbDriveName(),
		DataSourceName:  base2.BsfConfigInstance.GetDbDataSourceName(),
		MaxPoolSize:     base2.BsfConfigInstance.GetDbMaxPoolSize(),
		MinPoolSize:     base2.BsfConfigInstance.GetDbMinPoolSize(),
		ConnMaxLifetime: base2.BsfConfigInstance.GetDbConnMaxLifetime(),
	})
}
func (c *DbSource) CreatePool2(config base2.DbConfig) *DbSource {
	if config.DriverName == "" {
		panic(base2.NewBsfError("DriverName为空").Error())
	}

	if config.DataSourceName == "" {
		panic(base2.NewBsfError("DataSourceName未空").Error())
	}

	db, err := sqlx.Open(config.DriverName, config.DataSourceName)
	if err != nil {
		panic(base2.NewBsfError2("链接失败", err).Error())
	}

	if config.MaxPoolSize > 0 {
		db.SetMaxOpenConns(config.MaxPoolSize)
	}
	if config.MinPoolSize > 0 {
		db.SetMaxIdleConns(config.MinPoolSize)
	}
	if config.ConnMaxLifetime > 0 {
		db.SetConnMaxLifetime(config.ConnMaxLifetime)
	}

	if err := db.Ping(); err != nil {
		db.Close()
		panic(base2.NewBsfError("链接失败").Error())
	}
	return &DbSource{Db: db}
}
func (c *DbSource) GetConn() *DbConn {
	return &DbConn{dbSource: c, trans: nil}
}
func (c *DbSource) Close() error {
	if c.Db != nil {
		err := c.Db.Close()
		return err
	}
	return nil
}

type DbConn struct {
	dbSource *DbSource
	trans    *sqlx.Tx
}

func (c *DbConn) GetDb() *sqlx.DB {
	return c.dbSource.Db
}
func (c *DbConn) GetTrans() *sqlx.Tx {
	return c.trans
}

func (c *DbConn) GetLastInsertID(result sql.Result) int64 {
	r, err := result.LastInsertId()
	if err != nil {
		panic(err)
	}
	return r
}

func (c *DbConn) GetRowsAffected(result sql.Result) int64 {
	r, err := result.RowsAffected()
	if err != nil {
		panic(err)
	}
	return r
}

func (c *DbConn) Rows(dest interface{}, query string, args ...interface{}) {
	var e error
	start := time.Now()
	if c.trans != nil {
		e = c.trans.Select(dest, query, args...)
	} else {
		e = c.GetDb().Select(dest, query, args...)
	}
	c.log(start, query, args...)
	if e != nil {
		panic(c.errNew(e, query, args))
	}
}

func (c *DbConn) Scalar(dest interface{}, query string, args ...interface{}) {
	var e error
	start := time.Now()
	if c.trans != nil {
		e = c.trans.Get(dest, query, args...)
	} else {
		e = c.GetDb().Get(dest, query, args...)
	}
	c.log(start, query, args...)
	if e != nil {
		panic(c.errNew(e, query, args))
	}
}

func (c *DbConn) Exec(query string, args ...interface{}) sql.Result {
	var r sql.Result
	start := time.Now()
	var e error
	if c.trans != nil {
		r, e = c.trans.Exec(query, args)
	} else {
		r, e = c.GetDb().Exec(query, args...)
	}
	c.log(start, query, args...)
	if e != nil {

		panic(c.errNew(e, query, args))
	}
	return r
}

func (c *DbConn) Transaction(action func(conn *DbConn) any) any {
	tx, err := c.GetDb().Beginx()
	if err != nil {
		panic(base2.NewBsfError2("事务启动失败", err))
	}
	c.trans = tx
	defer func() {
		c.trans = nil
		if err2 := recover(); err2 != nil {
			tx.Rollback()
			panic(err2)
		}
	}()
	r := action(c)
	err3 := tx.Commit()
	if err3 != nil {
		panic(base2.NewBsfError2("事务提交失败", err3))
	}
	return r
}

func (c *DbConn) AppendSqlCondition(sb *strings.Builder, params *[]any, condition bool, sqlFormat string, args ...interface{}) {
	if condition {
		sb.WriteString(sqlFormat)
		*params = append(*params, args...)
	}
}
func (c *DbConn) log(startTime time.Time, query string, args ...interface{}) {
	if base2.BsfConfigInstance.GetDbSqlTimeLogEnabled() {
		utils.LogUtil.Info("sql:%v,parms:%v,time:%vms", query, args, time.Since(startTime).Milliseconds())
	}
}

func (c *DbConn) errNew(err error, query string, args ...interface{}) error {
	return base2.NewBsfError2(fmt.Sprintf("sql执行出错,sql:%v,parms:%v", query, args), err)
}
